package com.ttxs.uaa.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ttxs.common.core.vo.LoginUser;
import com.ttxs.common.core.vo.Token;
import com.ttxs.common.core.exception.RemoteLoginException;
import com.ttxs.common.core.utils.AuthUtils;
import com.ttxs.common.core.constant.Common;
import com.ttxs.uaa.entity.*;
import com.ttxs.uaa.exception.*;
import com.ttxs.uaa.mapper.SysMenuMapper;
import com.ttxs.uaa.mapper.SysRoleMapper;
import com.ttxs.uaa.mapper.SysUserMapper;
import com.ttxs.uaa.mapper.SysUserVoMapper;
import com.ttxs.uaa.service.ISysPlatformService;
import com.ttxs.uaa.service.ISysUserRoleService;
import com.ttxs.uaa.service.ISysUserService;
import com.ttxs.uaa.vo.ImportUserVO;
import com.ttxs.uaa.vo.LoginResult;
import com.ttxs.uaa.vo.UserVO;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author 天天向上
 * @since 2021-07-26
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {
    private final SysRoleMapper sysRoleMapper;
    private final SysMenuMapper sysMenuMapper;
    private final ISysUserRoleService sysUserRoleService;
    private final ISysPlatformService sysPlatformService;
    private final SysUserVoMapper sysUserVoMapper;

    private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    @Override
    public Page<UserVO> pageVo(Page<UserVO> page, UserVO condition) {
        LambdaQueryWrapper<UserVO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserVO::getPlatformId, condition.getPlatformId());
        if (condition.getDeptIds() != null) {
            queryWrapper.in(UserVO::getDeptId, Arrays.asList(condition.getDeptIds()));
        }
        queryWrapper.eq(condition.getSex() != null, UserVO::getSex, condition.getSex());
        queryWrapper.like(StringUtils.hasText(condition.getNickName()), UserVO::getNickName, condition.getNickName());
        queryWrapper.like(StringUtils.hasText(condition.getUserName()), UserVO::getUserName, condition.getUserName());
        queryWrapper.like(StringUtils.hasText(condition.getPhonenumber()), UserVO::getPhonenumber, condition.getPhonenumber());
        queryWrapper.like(StringUtils.hasText(condition.getStatus()), UserVO::getStatus, condition.getStatus());
        return sysUserVoMapper.selectPageVo(page, queryWrapper);
    }

    @Override
    @Transactional
    public boolean register(SysUser sysUser) {
        // 查询平台状态
        SysPlatform sysPlatform = sysPlatformService.getById(sysUser.getPlatformId());
        if (sysPlatform.getStatus().equals(Common.Status.STATUS_DISABLED)) {
            throw new DisabledException("该平台已经禁用，请联系管理员");
        }
        // 查询用户是否存在
        Long count = getBaseMapper().selectCount(new QueryWrapper<SysUser>()
                .eq("platform_id", sysUser.getPlatformId())
                .eq("user_name", sysUser.getUserName()));
        if (count > 0) {
            throw new DuplicateException(String.format("\"%s\"已经存在", sysUser.getUserName()));
        }
//        sysUser.setStatus(Common.Status.STATUS_DISABLED);
        sysUser.setPassword(passwordEncoder.encode(sysUser.getPassword()));
        sysUser.setCreateTime(LocalDateTime.now());
        super.save(sysUser);
        // 保存角色
        List<SysUserRole> list = Arrays.stream(sysUser.getRoleIds()).map(id -> new SysUserRole(sysUser.getUserId(), id, sysUser.getPlatformId())).collect(Collectors.toList());
        sysUserRoleService.saveBatch(list, list.size());
        return true;
    }

    @SneakyThrows
    @Override
    public LoginResult login(String platformName, String username, String password) {
        LoginResult loginResult = new LoginResult();
        SysPlatform sysPlatform = sysPlatformService.getByPlatformName(platformName);
        if (sysPlatform == null) {
            return loginResult.setMessage("平台不存在");
        }
        if (sysPlatform.getStatus().equals(Common.Status.STATUS_DISABLED)) {
            return loginResult.setMessage("平台已停用");
        }
        SysUser user = getBaseMapper().selectOne(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getUserName, username)
                .eq(SysUser::getPlatformId, sysPlatform.getPlatformId()));
        if (user == null) {
            loginResult.setMessage("用户不存在");
        } else if (!passwordEncoder.matches(password, user.getPassword())) {
            loginResult.setMessage("密码不正确");
        } else if (user.getStatus().equals(Common.Status.STATUS_DISABLED)) {
            loginResult.setMessage("用户已停用");
        } else {
            // 获取用户角色名字列表，转换成字符串，用英文逗号分割
            String roleNames = getRoleNames(user);
            Token token = createToken(sysPlatform, user, roleNames);
            // 以下业务待优化，需要简化操作
            loginResult.setTokenCreateTime(token.getGenTime());
            loginResult.setTokenExpireTime(token.getExpTime());
            loginResult.setToken(token.getToken());
            // 创建refreshToken
            loginResult.setRefreshToken(token.getToken());
            loginResult.setRefreshTokenExpireTime(token.getExpTime() + 7200 * 1000);
            // 角色名字中文转码，以便前端存储到request header
            loginResult.setRoleNames(URLEncoder.encode(roleNames, "UTF-8"));
            // 传递其他必要参数到前端
            loginResult.setPlatformId(sysPlatform.getPlatformId());
            loginResult.setPlatformName(platformName);
            loginResult.setSuccess(true);
            loginResult.setUserId(user.getUserId());
            loginResult.setNickName(user.getNickName());
        }
        return loginResult;
    }

    @Override
    public void resetPassword(Long userId, String userName, String platformId) {
        SysUser user = getById(userId);
        user.setUserName(userName);
        user.setPlatformId(platformId);
        user.setPassword(passwordEncoder.encode(Common.Global.DEFAULT_PASSWORD));
        int success = getBaseMapper().resetPassword(user);
        if (success != 1) throw new ResetPasswordException(String.format("重置【%s】密码失败", userName));
    }

    @Override
    public void changePassword(Long userId, String platformId, String newPassword, String oldPassword) {
        SysUser user = getById(userId);
        if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
            throw new IncorrectPasswordException("密码不正确");
        }

        int success = getBaseMapper().changePassword(userId, platformId, passwordEncoder.encode(newPassword), user.getPassword());
        if (success != 1) throw new ChangePasswordException(String.format("修改【%s】密码失败", user.getNickName()));
    }

    @Override
    public SysUser getWithRoles(Long userId) {
        SysUser sysUser = this.getById(userId);
        List<SysRole> sysRoles = this.sysRoleMapper.selectByUserId(sysUser.getUserId());
        sysUser.setRoles(sysRoles);
        return sysUser;
    }

    @Override
    @Transactional
    public void updateByAim(SysUser sysUser) {
        String aim = sysUser.getAim();
        Assert.hasText(aim, "aim不能为空");
        if (aim.equals(SysUser.AIM_USER)) {
            getBaseMapper().updateById(sysUser);
        } else if (aim.equals(SysUser.AIM_STATUS)) {
            getBaseMapper().changeStatus(sysUser);
        } else if (aim.equals(SysUser.AIM_WITH_ROLES)) {
            this.updateById(sysUser);
            // 删除用户关联的角色
            this.sysUserRoleService.remove(new LambdaQueryWrapper<SysUserRole>()
                    .eq(SysUserRole::getUserId, sysUser.getUserId()));
            // 将角色ID数组转换成关联表的实体对象集合
            List<SysUserRole> userRoleList = new ArrayList<>();
            for (Long roleId : sysUser.getRoleIds()) {
                SysUserRole userRole = new SysUserRole(sysUser.getUserId(), roleId, sysUser.getPlatformId());
                userRoleList.add(userRole);
            }
            // 批量保存用户关联的角色
            this.sysUserRoleService.saveBatch(userRoleList);
        }
    }

    @Override
    public boolean removeByIds(Long[] idList) {
        List<Long> ids = Arrays.asList(idList);
        // 删除用户角色关联数据
        sysUserRoleService.removeByUserIds(ids);
        return super.removeByIds(ids);
    }

    @Override
    public boolean changeStatus(SysUser sysUser) {
        return getBaseMapper().changeStatus(sysUser) > 0;
    }

    @Transactional
    @Override
    public boolean importUsers(String platformName, List<ImportUserVO> users) {
        Assert.notEmpty(users, "users must not be null");
        Set<Long> roleIds = users.stream().map(item -> item.getRoleIds())
                .flatMap(Arrays::stream)
                .collect(Collectors.toSet());
        // 验证角色是否存在
        Set<Long> dbRoleIds = sysRoleMapper.selectBatchIds(roleIds).stream().map(SysRole::getRoleId).collect(Collectors.toSet());
        roleIds.removeAll(dbRoleIds);
        Assert.isTrue(roleIds.isEmpty(), "role id " + roleIds + "not found");

        SysPlatform sysPlatform = getSysPlatform(platformName);
        List<SysUser> userList = users.stream().map(item -> {
            SysUser user = new SysUser();
            user.setUserName(item.getUsername());
            user.setPassword(item.getEncodedPassword());
            user.setNickName(item.getNickName());
            user.setPlatformId(sysPlatform.getPlatformId());
            user.setRoleIds(item.getRoleIds());
            return user;
        }).collect(Collectors.toList());
        // 保存用户
        this.saveBatch(userList);
        // 保存用户角色
        List<SysUserRole> userRoleList = new ArrayList<>();
        userList.forEach(user -> {
            for (Long roleId : user.getRoleIds()) {
                SysUserRole userRole = new SysUserRole();
                userRole.setRoleId(roleId);
                userRole.setUserId(user.getUserId());
                userRole.setPlatformId(sysPlatform.getPlatformId());
                userRoleList.add(userRole);
            }
        });
        sysUserRoleService.saveBatch(userRoleList);
        return true;
    }

    // 创建token，并缓存loginUser到redis
    private Token createToken(SysPlatform sysPlatform, SysUser user, String roleNames) {
        Set<String> urlPatterns =
                sysMenuMapper.selectListByRoleNames(user.getPlatformId(), roleNames.split(",")).stream()
                        .filter(item -> StringUtils.hasText(item.getApiUrl()))
                        .map(item -> getMethodAndUrl(item)).sorted()
                        .collect(Collectors.toCollection(LinkedHashSet::new));

        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(user.getUserId());
        loginUser.setUsername(user.getUserName());
        loginUser.setNickName(user.getNickName());
        loginUser.setAuthorities(urlPatterns);
        loginUser.getExtra().put(Common.HttpHeaders.PLATFORM_ID, sysPlatform.getPlatformId());
        loginUser.getExtra().put(Common.HttpHeaders.PLATFORM_NAME, sysPlatform.getPlatformName());
        // 创建token和refreshToken，并缓存权限到redis
        return AuthUtils.createToken(loginUser);
    }

    private String getMethodAndUrl(SysMenu item) {
        if (StringUtils.hasText(item.getHttpMethod())) {
            return item.getHttpMethod().toUpperCase() + "_" + item.getApiUrl();
        }
        return "GET_" + item.getApiUrl();
    }

    private SysPlatform getSysPlatform(String platformName) {
        SysPlatform sysPlatform = sysPlatformService.getByPlatformName(platformName);
        if (sysPlatform == null) {
            throw new RemoteLoginException("平台不存在");
        }
        if (sysPlatform.getStatus().equals(Common.Status.STATUS_DISABLED)) {
            throw new RemoteLoginException("平台已停用");
        }
        return sysPlatform;
    }

    private String getRoleNames(SysUser user) {
        List<SysRole> sysRoles = sysRoleMapper.selectByUserId(user.getUserId());
        return sysRoles.stream().map(r -> r.getRoleName()).collect(Collectors.joining(","));
    }

}
